/*
+ * based on linux-2.6.10/arch/i386/kernel/apic.c
+ *
* Local APIC handling, local APIC timers
*
* (c) 1999, 2000 Ingo Molnar <mingo@redhat.com>
* for testing these extensively.
* Maciej W. Rozycki : Various updates and fixes.
* Mikael Pettersson : Power Management for UP-APIC.
+ * Pavel Machek and
+ * Mikael Pettersson : PM converted to driver model.
*/
-
#include <xen/config.h>
-#include <xen/ac_timer.h>
#include <xen/perfc.h>
#include <xen/errno.h>
#include <xen/init.h>
#include <asm/hardirq.h>
#include <asm/apic.h>
#include <asm/io_apic.h>
-
+#include <asm/mach_apic.h>
+#include <asm/io_ports.h>
/* Using APIC to generate smp_local_timer_interrupt? */
int using_apic_timer = 0;
apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
}
+#if 0
+/* lets not touch this if we didn't frob it */
+#ifdef CONFIG_X86_MCE_P4THERMAL
+ if (maxlvt >= 5) {
+ v = apic_read(APIC_LVTTHMR);
+ apic_write_around(APIC_LVTTHMR, v | APIC_LVT_MASKED);
+ }
+#endif
+#endif
+
/*
* Clean APIC state for other OSs:
*/
apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
if (maxlvt >= 4)
apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+
+#if 0
+#ifdef CONFIG_X86_MCE_P4THERMAL
+ if (maxlvt >= 5)
+ apic_write_around(APIC_LVTTHMR, APIC_LVT_MASKED);
+#endif
+#endif
+
v = GET_APIC_VERSION(apic_read(APIC_LVR));
if (APIC_INTEGRATED(v)) { /* !82489DX */
- if (maxlvt > 3)
+ if (maxlvt > 3) /* Due to Pentium errata 3AP and 11AP. */
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
}
outb(0x70, 0x22);
outb(0x01, 0x23);
}
+#if 0
+ enable_apic_mode();
+#endif
}
void disconnect_bsp_APIC(void)
*/
reg0 = apic_read(APIC_ID);
Dprintk("Getting ID: %x\n", reg0);
- apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);
- reg1 = apic_read(APIC_ID);
- Dprintk("Getting ID: %x\n", reg1);
- apic_write(APIC_ID, reg0);
- if (reg1 != (reg0 ^ APIC_ID_MASK))
- return 0;
/*
* The next two are just to see if we have sane values.
void __init sync_Arb_IDs(void)
{
+ /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */
+ unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR));
+ if (ver >= 0x14) /* P4 or higher */
+ return;
/*
* Wait for idle.
*/
{
}
-static unsigned long calculate_ldr(unsigned long old)
-{
- unsigned long id = 1UL << smp_processor_id();
- return (old & ~APIC_LDR_MASK)|SET_APIC_LOGICAL_ID(id);
-}
-
void __init setup_local_APIC (void)
{
- unsigned long value, ver, maxlvt;
+ unsigned long oldvalue, value, ver, maxlvt;
+
+ /* Pound the ESR really hard over the head with a big hammer - mbligh */
+ if (esr_disable) {
+ apic_write(APIC_ESR, 0);
+ apic_write(APIC_ESR, 0);
+ apic_write(APIC_ESR, 0);
+ apic_write(APIC_ESR, 0);
+ }
value = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(value);
if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)
__error_in_apic_c();
- /* Double-check wether this APIC is really registered. */
- if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
+ /*
+ * Double-check whether this APIC is really registered.
+ */
+ if (!apic_id_registered())
BUG();
/*
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
* document number 292116). So here it goes...
*/
-
- /*
- * In clustered apic mode, the firmware does this for us
- * Put the APIC into flat delivery mode.
- * Must be "all ones" explicitly for 82489DX.
- */
- apic_write_around(APIC_DFR, APIC_DFR_FLAT);
-
- /*
- * Set up the logical destination ID.
- */
- value = apic_read(APIC_LDR);
- apic_write_around(APIC_LDR, calculate_ldr(value));
+ init_apic_ldr();
/*
* Set Task Priority to 'accept all'. We never change this
*/
value |= APIC_SPIV_APIC_ENABLED;
+ /*
+ * Some unknown Intel IO/APIC (or APIC) errata is biting us with
+ * certain networking cards. If high frequency interrupts are
+ * happening on a particular IOAPIC pin, plus the IOAPIC routing
+ * entry is masked/unmasked at a high rate as well then sooner or
+ * later IOAPIC line gets 'stuck', no more interrupts are received
+ * from the device. If focus CPU is disabled then the hang goes
+ * away, oh well :-(
+ *
+ * [ This bug can be reproduced easily with a level-triggered
+ * PCI Ne2000 networking cards and PII/PIII processors, dual
+ * BX chipset. ]
+ */
+ /*
+ * Actually disabling the focus CPU check just makes the hang less
+ * frequent as it makes the interrupt distributon model be more
+ * like LRU than MRU (the short-term load is more even across CPUs).
+ * See also the comment in end_level_ioapic_irq(). --macro
+ */
+#if 1
/* Enable focus processor (bit==0) */
value &= ~APIC_SPIV_FOCUS_DISABLED;
-
- /* Set spurious IRQ vector */
+#else
+ /* Disable focus processor (bit==1) */
+ value |= APIC_SPIV_FOCUS_DISABLED;
+#endif
+ /*
+ * Set spurious IRQ vector
+ */
value |= SPURIOUS_APIC_VECTOR;
apic_write_around(APIC_SPIV, value);
* TODO: set up through-local-APIC from through-I/O-APIC? --macro
*/
value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
- if (!smp_processor_id()) {
+ if (!smp_processor_id() && (pic_mode || !value)) {
value = APIC_DM_EXTINT;
printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
} else {
value |= APIC_LVT_LEVEL_TRIGGER;
apic_write_around(APIC_LVT1, value);
- if (APIC_INTEGRATED(ver)) { /* !82489DX */
+ if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */
maxlvt = get_maxlvt();
if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
apic_write(APIC_ESR, 0);
- value = apic_read(APIC_ESR);
- printk("ESR value before enabling vector: %08lx\n", value);
+ oldvalue = apic_read(APIC_ESR);
- value = ERROR_APIC_VECTOR; /* enables sending errors */
+ value = ERROR_APIC_VECTOR; // enables sending errors
apic_write_around(APIC_LVTERR, value);
- /* spec says clear errors after enabling vector. */
+ /*
+ * spec says clear errors after enabling vector.
+ */
if (maxlvt > 3)
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
- printk("ESR value after enabling vector: %08lx\n", value);
+ if (value != oldvalue)
+ printk("ESR value before enabling vector: 0x%08lx "
+ "after: 0x%08lx\n", oldvalue, value);
} else {
+ if (esr_disable)
+ /*
+ * Something untraceble is creating bad interrupts on
+ * secondary quads ... for the moment, just leave the
+ * ESR disabled - we can't do anything useful with the
+ * errors anyway - mbligh
+ */
+ printk("Leaving ESR disabled.\n");
+ else
printk("No ESR for 82489DX.\n");
}
- if ( (smp_processor_id() == 0) && (nmi_watchdog == NMI_LOCAL_APIC) )
+ if (nmi_watchdog == NMI_LOCAL_APIC)
setup_apic_nmi_watchdog();
+#if 0
+ apic_pm_activate();
+#endif
}
-
-static inline void apic_pm_init1(void) { }
-static inline void apic_pm_init2(void) { }
-
-
/*
* Detect and enable local APICs on non-SMP boards.
* Original code written by Keir Fraser.
u32 h, l, features;
extern void get_cpu_vendor(struct cpuinfo_x86*);
+#if 0
+ /* Disabled by kernel option? */
+ if (enable_local_apic < 0)
+ return -1;
+#endif
+
/* Workaround for us being called before identify_cpu(). */
get_cpu_vendor(&boot_cpu_data);
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1)
- break;
- if (boot_cpu_data.x86 == 15 && cpu_has_apic)
+ if ((boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1) ||
+ (boot_cpu_data.x86 == 15))
break;
goto no_apic;
case X86_VENDOR_INTEL:
- if (boot_cpu_data.x86 == 6 ||
- (boot_cpu_data.x86 == 15 && cpu_has_apic) ||
+ if (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15 ||
(boot_cpu_data.x86 == 5 && cpu_has_apic))
break;
goto no_apic;
}
if (!cpu_has_apic) {
+ /*
+ * Over-ride BIOS and try to enable the local
+ * APIC only if "lapic" specified.
+ */
+#if 0
+ if (enable_local_apic <= 0) {
+ printk("Local APIC disabled by BIOS -- "
+ "you can enable it with \"lapic\"\n");
+ return -1;
+ }
+#endif
/*
* Some BIOSes disable the local APIC in the
* APIC_BASE MSR. This can only be done in
- * software for Intel P6 and AMD K7 (Model > 1).
+ * software for Intel P6 or later and AMD K7
+ * (Model > 1) or later.
*/
rdmsr(MSR_IA32_APICBASE, l, h);
if (!(l & MSR_IA32_APICBASE_ENABLE)) {
enabled_via_apicbase = 1;
}
}
-
- /* The APIC feature bit should now be enabled in `cpuid' */
+ /*
+ * The APIC feature bit should now be enabled
+ * in `cpuid'
+ */
features = cpuid_edx(1);
if (!(features & (1 << X86_FEATURE_APIC))) {
printk("Could not enable APIC!\n");
return -1;
}
-
- set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability);
+ set_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
- boot_cpu_physical_apicid = 0;
/* The BIOS may have set up the APIC at some other address */
rdmsr(MSR_IA32_APICBASE, l, h);
nmi_watchdog = NMI_LOCAL_APIC;
printk("Found and enabled local APIC!\n");
- apic_pm_init1();
+
+#if 0
+ apic_pm_activate();
+#endif
+
return 0;
- no_apic:
+no_apic:
printk("No local APIC present or hardware disabled\n");
return -1;
}
void __init init_apic_mappings(void)
{
- unsigned long apic_phys = 0;
+ unsigned long apic_phys;
/*
- * If no local APIC can be found then set up a fake all zeroes page to
- * simulate the local APIC and another one for the IO-APIC.
+ * If no local APIC can be found then set up a fake all
+ * zeroes page to simulate the local APIC and another
+ * one for the IO-APIC.
*/
if (!smp_found_config && detect_init_APIC()) {
apic_phys = alloc_xenheap_page();
#ifdef CONFIG_X86_IO_APIC
{
- unsigned long ioapic_phys = 0, idx = FIX_IO_APIC_BASE_0;
+ unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
int i;
for (i = 0; i < nr_ioapics; i++) {
- if (smp_found_config)
+ if (smp_found_config) {
ioapic_phys = mp_ioapics[i].mpc_apicaddr;
+ if (!ioapic_phys) {
+ printk(KERN_ERR
+ "WARNING: bogus zero IO-APIC "
+ "address found in MPTABLE, "
+ "disabling IO/APIC support!\n");
+ smp_found_config = 0;
+ skip_ioapic_setup = 1;
+ goto fake_ioapic_page;
+ }
+ } else {
+fake_ioapic_page:
+ ioapic_phys = alloc_xenheap_page();
+ ioapic_phys = __pa(ioapic_phys);
+ }
set_fixmap_nocache(idx, ioapic_phys);
Dprintk("mapped IOAPIC to %08lx (%08lx)\n",
fix_to_virt(idx), ioapic_phys);
{
/*extern spinlock_t i8253_lock;*/
/*unsigned long flags;*/
+
unsigned int count;
+
/*spin_lock_irqsave(&i8253_lock, flags);*/
- outb_p(0x00, 0x43);
- count = inb_p(0x40);
- count |= inb_p(0x40) << 8;
+
+ outb_p(0x00, PIT_MODE);
+ count = inb_p(PIT_CH0);
+ count |= inb_p(PIT_CH0) << 8;
+
/*spin_unlock_irqrestore(&i8253_lock, flags);*/
+
return count;
}
-void __init wait_8254_wraparound(void)
+/* next tick in 8254 can be caught by catching timer wraparound */
+static void __init wait_8254_wraparound(void)
{
unsigned int curr_count, prev_count=~0;
int delta;
+
curr_count = get_8254_timer_count();
+
do {
prev_count = curr_count;
curr_count = get_8254_timer_count();
delta = curr_count-prev_count;
+
/*
- * This limit for delta seems arbitrary, but it isn't, it's slightly
- * above the level of error a buggy Mercury/Neptune chipset timer can
- * cause.
+ * This limit for delta seems arbitrary, but it isn't, it's
+ * slightly above the level of error a buggy Mercury/Neptune
+ * chipset timer can cause.
*/
+
} while (delta < 300);
}
+/*
+ * Default initialization for 8254 timers. If we use other timers like HPET,
+ * we override this later
+ */
+void (*wait_timer_tick)(void) = wait_8254_wraparound;
+
/*
* This function sets up the local APIC timer, with a timeout of
* 'clocks' APIC bus clock. During calibration we actually call
* We do reads before writes even if unnecessary, to get around the
* P5 APIC double write bug.
*/
+
#define APIC_DIVISOR 1
+
static void __setup_APIC_LVTT(unsigned int clocks)
{
- unsigned int lvtt1_value, tmp_value;
- lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV)|LOCAL_TIMER_VECTOR;
- apic_write_around(APIC_LVTT, lvtt1_value);
+ unsigned int lvtt_value, tmp_value, ver;
+
+ ver = GET_APIC_VERSION(apic_read(APIC_LVR));
+ lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
+ if (!APIC_INTEGRATED(ver))
+ lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV);
+ apic_write_around(APIC_LVTT, lvtt_value);
+
+#if 0
+ /*
+ * Divide PICLK by 16
+ */
+ tmp_value = apic_read(APIC_TDCR);
+ apic_write_around(APIC_TDCR, (tmp_value
+ & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
+ | APIC_TDR_DIV_16);
+#endif
+
tmp_value = apic_read(APIC_TDCR);
apic_write_around(APIC_TDCR, (tmp_value | APIC_TDR_DIV_1));
+
apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
}
/*
* In this function we calibrate APIC bus clocks to the external timer.
*
- * As a result we have the Bys Speed and CPU speed in Hz.
+ * As a result we have the Bus Speed and CPU speed in Hz.
*
* We want to do the calibration only once (for CPU0). CPUs connected by the
* same APIC bus have the very same bus frequency.
int i;
const int LOOPS = HZ/10;
+#if 0
+ apic_printk(APIC_VERBOSE, "calibrating APIC timer ...\n");
+#endif
printk("Calibrating APIC timer for CPU%d...\n", smp_processor_id());
- /* Put whatever arbitrary (but long enough) timeout
+ /*
+ * Put whatever arbitrary (but long enough) timeout
* value into the APIC clock, we just want to get the
- * counter running for calibration. */
+ * counter running for calibration.
+ */
__setup_APIC_LVTT(1000000000);
- /* The timer chip counts down to zero. Let's wait
+ /*
+ * The timer chip counts down to zero. Let's wait
* for a wraparound to start exact measurement:
- * (the current tick might have been already half done) */
- wait_8254_wraparound();
+ * (the current tick might have been already half done)
+ */
- /* We wrapped around just now. Let's start: */
+ wait_timer_tick();
+
+ /*
+ * We wrapped around just now. Let's start:
+ */
+ if (cpu_has_tsc)
rdtscll(t1);
tt1 = apic_read(APIC_TMCCT);
- /* Let's wait LOOPS wraprounds: */
+ /*
+ * Let's wait LOOPS wraprounds:
+ */
for (i = 0; i < LOOPS; i++)
- wait_8254_wraparound();
+ wait_timer_tick();
tt2 = apic_read(APIC_TMCCT);
+ if (cpu_has_tsc)
rdtscll(t2);
- /* The APIC bus clock counter is 32 bits only, it
+ /*
+ * The APIC bus clock counter is 32 bits only, it
* might have overflown, but note that we use signed
* longs, thus no extra care needed.
- * underflown to be exact, as the timer counts down ;) */
- result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
+ *
+ * underflown to be exact, as the timer counts down ;)
+ */
- printk("..... CPU speed is %ld.%04ld MHz.\n",
- ((long)(t2-t1)/LOOPS) / (1000000/HZ),
- ((long)(t2-t1)/LOOPS) % (1000000/HZ));
+ result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
- printk("..... Bus speed is %ld.%04ld MHz.\n",
- result / (1000000/HZ),
- result % (1000000/HZ));
+ if (cpu_has_tsc)
+ printk("..... CPU clock speed is %ld.%04ld MHz.\n",
+ ((long)(t2-t1)/LOOPS)/(1000000/HZ),
+ ((long)(t2-t1)/LOOPS)%(1000000/HZ));
- /*
- * KAF: Moved this to time.c where it's calculated relative to the TSC.
- * Therefore works on machines with no local APIC.
- */
- /*cpu_freq = (u64)(((t2-t1)/LOOPS)*HZ);*/
+ printk("..... host bus clock speed is %ld.%04ld MHz.\n",
+ result/(1000000/HZ),
+ result%(1000000/HZ));
/* set up multipliers for accurate timer code */
bus_freq = result*HZ;
printk("..... bus_scale = 0x%08X\n", bus_scale);
/* reset APIC to zero timeout value */
__setup_APIC_LVTT(0);
+
return result;
}
raise_softirq(AC_TIMER_SOFTIRQ);
}
+
/*
* This interrupt should _never_ happen with our APIC/SMP architecture
*/
-asmlinkage void smp_spurious_interrupt(void)
+asmlinkage void smp_spurious_interrupt(struct xen_regs *regs)
{
unsigned long v;
+#if 0
+ irq_enter();
+#endif
/*
* Check if this really is a spurious interrupt and ACK it
* if it is a vectored one. Just in case...
/* see sw-dev-man vol 3, chapter 7.4.13.5 */
printk("spurious APIC interrupt on CPU#%d, should never happen.\n",
smp_processor_id());
+#if 0
+ irq_exit();
+#endif
}
/*
* This interrupt should never happen with our APIC/SMP architecture
*/
-asmlinkage void smp_error_interrupt(void)
+asmlinkage void smp_error_interrupt(struct xen_regs *regs)
{
unsigned long v, v1;
+#if 0
+ irq_enter();
+#endif
/* First tickle the hardware, only then report what went on. -- REW */
v = apic_read(APIC_ESR);
apic_write(APIC_ESR, 0);
6: Received illegal vector
7: Illegal register address
*/
- printk ("APIC error on CPU%d: %02lx(%02lx)\n",
+ Dprintk("APIC error on CPU%d: %02lx(%02lx)\n",
smp_processor_id(), v , v1);
+#if 0
+ irq_exit();
+#endif
}
/*
*/
int __init APIC_init_uniprocessor (void)
{
+#if 0
+ if (enable_local_apic < 0)
+ clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
+#endif
+
if (!smp_found_config && !cpu_has_apic)
return -1;
/*
* Complain if the BIOS pretends there is one.
*/
- if (!cpu_has_apic&&APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]))
- {
+ if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
printk("BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
return -1;
#ifdef CONFIG_SMP
cpu_online_map = 1;
+#endif
+#if 0
+ phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid);
#endif
phys_cpu_present_map = 1;
apic_write_around(APIC_ID, boot_cpu_physical_apicid);
- apic_pm_init2();
-
setup_local_APIC();
+#if 0
+ if (nmi_watchdog == NMI_LOCAL_APIC)
+ check_nmi_watchdog();
+#endif
#ifdef CONFIG_X86_IO_APIC
- if (smp_found_config && nr_ioapics)
+ if (smp_found_config)
+ if (!skip_ioapic_setup && nr_ioapics)
setup_IO_APIC();
+#endif
+#if 0
+ setup_boot_APIC_clock();
#endif
setup_APIC_clocks();